﻿#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

//****************************************************************************************1 为什么使⽤⽂件？

//****************************************************************************************2 什么是⽂件？

//**********************2.1 程序⽂件
//**********************2.2 数据⽂件
//**********************2.3 ⽂件名

//****************************************************************************************3 ⼆进制⽂件和⽂本⽂件？

//例如存储10000

//二进制文件
//int main()
//{
//	int a = 10000;
//
//	FILE * pf = fopen("test.txt", "wb");
//	fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//****************************************************************************************4 ⽂件的打开和关闭

//**********************4.1 流和标准流

//***********4.1.1 流
//***********4.1.2 标准流

//**********************4.2 ⽂件指针
//**********************4.3 ⽂件的打开和关闭
//打开文件
//FILE* fopen(const char* filename, const char* mode);
//关闭⽂件
//int fclose(FILE* stream)

//int main()
//{
//	//打开文件
//	FILE* pf = fopen("test.txt", "w");
//	//打开文件成功，返回有效的地址
//	//如果返回失败，返回空指针
//	//所以要对返回的指针进行检查
//
//	//test.txt如果放在桌面上，则要在前面加上完整路径
////绝对路径
//	FILE* pf = fopen("C:\\Users\\wgl8o\\Desktop\\test.txt", "w");//双斜杠避免转义字符
////相对路径
//	// . 表示当前路径
//	// .. 表示上一级路径
//	FILE* pf = fopen(".\\..\\..\\test.txt", "w");
//	
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//...
//
//	//关闭文件
//	fclose(pf);//关闭文件后,pf成为野指针
//	//因此，pf要置空
//	pf = NULL;
//	return 0;
//}


//****************************************************************************************5 ⽂件的顺序读写
//**********************5.1 顺序读写函数介绍

//int fputc(int c, FILE* stream);

//int fgetc( FILE *stream );
//读取失败时，会返回EOF(end of file)----本质上是-1

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//写文件
//	//fputc('a', pf);//每写入一个字符，光标向后移动一个字符的单位
//	//fputc('b', pf);
//	//fputc('c', pf);
//	//fputc('d', pf);
//	//fputc('e', pf);
//	//fputc('f', pf);
//	//fputc('g', pf);
//	//fputc('j', pf);
//
//	//for (int i = 'a'; i <= 'z'; i++)
//	//{
//	//	fputc(i, pf);
//	//}
//
//	//int ch = fgetc(pf);//每读取一个字符，光标向后移动一个字符的单位
//	//printf("%c\n", ch);
//	//ch = fgetc(pf);
//	//printf("%c\n", ch); 
//	//ch = fgetc(pf);
//	//printf("%c\n", ch); 
//	//ch = fgetc(pf);
//	//printf("%c\n", ch); 
//	//ch = fgetc(pf);
//	//printf("%c\n", ch);
//
//	//int ch;
//	//for (int i = 'a'; i <= 'z'; i++)
//	//{
//	//	ch = fgetc(pf);
//	//	printf("%c\n", ch);
//	//}
//
//	int ch = 0;
//	while ((ch = fgetc(pf)) != EOF)//利用这样的循环可以输出文件中的所有内容,即使遇到换行等也会打印，除非遇到文件结束标志
//	{
//		printf("%c", ch);
//	}
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}


//int main()
//{
//	//由于这两个函数适用于所有输入/输出流，自然也就可以适用于标准输入(stdin)/输出流(stdout)
//	int ch = fgetc(stdin);//从键盘（标准输入流）上读取
//	fputc(ch, stdout);//将读取的内容输出（写）到屏幕上（标准输出流）
//	return 0;
//}

//int fputs(const char* string, FILE* stream);
//char *fgets( char *string, int n, FILE *stream );//返回值：读取成功，返回string这个地址；读取失败，返回NULL

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//写文件
//	//fputs("today is good", pf);
//	//fputs("what are doing", pf);//两句话仍在一行上
//	//fputs("today is good!\n", pf);
//	//fputs("你what are you doing?\n", pf);//两句话各自一行
//
//	//读文件
//	char arr[50] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";//便于区分
//	fgets(arr, 10, pf);//fgets会读取文件中读取num-1个字符（或遇到换行符时结束），最后一个放'\0'表示结束
//	printf("%s\n", arr);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//int main()
//{
//	//fgets/fputs也可以用于标准输入/输出流
//	char arr[20] = { 0 };
//	fgets(arr, 10, stdin);
//	fputs(arr, stdout);
//	return 0;
//}

//int printf( const char *format [, argument]... );//... 可变参数列表
//int fprintf( FILE *stream, const char *format [, argument ]...);
//int sprintf(char* buffer, const char* format[, argument] ...);//把格式化的数据写到字符串中

//struct S
//{
//	char name[20];
//	int age;
//	float score;
//};

//int main()
//{
//	//char name[20] = "zhangsan";
//	//int age = 20;
//	//float score = 95.5f;
//	struct S s = { "lisi", 18, 88.8f };
//
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//写文件
//	//printf("%s %d %.1f", name, age, score);
//	//fprintf(pf, "%s %d %.1f", s.name, s.age, s.score);
//	//读文件
//	//scanf("%s %d %f", s.name, &(s.age), &(s.score));
//	//fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));//fprintf/fscanf 两者使用时顺序要对应
//	//printf("%s %d %.1f", s.name, s.age, s.score);
//
//	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//	fprintf(stdout, "%s %d %.1f", s.name, s.age, s.score);//输出到标准输出流（屏幕）
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//int main()
//{
//	char arr[100] = { 0 };
//	struct S s = { "wangwu" ,22, 92.5f };
//
//	//临时变量
//	struct S tmp = { 0 };
//	//将s中的各个数据转化成字符串，存放在arr中
//	sprintf(arr, "%s %d %.1f", s.name, s.age, s.score);//sprintf 将格式化的数据写到字符串中
//	//从arr中提取格式化的数据，存放在tmp中
//	sscanf(arr, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));//sprintf/sscanf 两者使用时顺序要对应
//	printf("%s %d %.1f", tmp.name, tmp.age, tmp.score);
//	//printf("%s\n", arr);
//	return 0;
//}


//int scanf( const char *format [,argument]... );
//int fscanf( FILE *stream, const char *format [, argument ]... );
//int sscanf(const char* buffer, const char* format[, argument] ...);//从字符串中提取格式化的数据


//size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
//size_t fread( void *buffer, size_t size, size_t count, FILE *stream );//返回值：成功读取的数量


//struct S
//{
//	char name[20];
//	int age;
//	float score;
//};
//
//int main()
//{
//	//struct S s = { "cuihua" ,25, 88.8f };
//	struct S s = { 0 };
//	//把信息从buffer以二进制的形式写入到文件中
//	//从文件中读取二进制的信息到buffer中
//
//	//打开文件
//	FILE* pf = fopen("test.txt", "rb");//注意：打开方式为 以二进制写
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//fwrite(&s, sizeof(struct S), 1, pf);
//	fread(&s, sizeof(struct S), 1, pf);
//	printf("%s %d %.1f", s.name, s.age, s.score);
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}



//**********************5.2 对⽐⼀组函数
//见上

//****************************************************************************************6 ⽂件的随机读写
//**********************6.1 fseek

//int fseek( FILE *stream, long offset, int origin );
//根据⽂件指针的位置和偏移量来定位⽂件指针（⽂件内容的光标）

//offsetof --- 计算结构体成员相较于起始位置的偏移量

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//读文件
//	int ch = 0;
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	//此时我想直接读e，那么就需要我们利用fseek来定位文件指针
//	//fseek(pf, 4, SEEK_SET);
//	//fseek(pf, 1, SEEK_CUR);
//	fseek(pf, -2, SEEK_END);
//
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//  fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

//**********************6.2 ftell

//long ftell(FILE* stream);
//返回⽂件指针相对于起始位置的偏移量


////int main()
////{
////	FILE* pf = fopen("test.txt", "r");
////	if (pf == NULL)
////	{
////		perror("fopen");
////		return 1;
////	}
////
////	//读文件
////	int ch = 0;
////	ch = fgetc(pf);
////	printf("%c\n", ch);
////	ch = fgetc(pf);
////	printf("%c\n", ch);
////	ch = fgetc(pf);
////	printf("%c\n", ch);
////
////	//此时我想直接读e，那么就需要我们利用fseek来定位文件指针
////	//fseek(pf, 4, SEEK_SET);
////	//fseek(pf, 1, SEEK_CUR);
////	fseek(pf, -2, SEEK_END);
////	ch = fgetc(pf);
////	printf("%c\n", ch);
////	//输出文件指针相较于起始位置的偏移量
////	printf("%d\n", ftell(pf));
////	 
////	//让⽂件指针的位置回到⽂件的起始
////	rewind(pf);
////	ch = fgetc(pf);
////	printf("%c\n", ch);
////
////	fclose(pf);
////	pf = NULL;
////
////	return 0;
////}


//**********************6.3 rewind

//void rewind(FILE * stream);
//让⽂件指针的位置回到⽂件的起始
//见上


//****************************************************************************************7 ⽂件读取结束的判定

//**********************7.1 被错误使⽤的 feof
/*
牢记：在⽂件读取过程中，不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是：当⽂件读取结束的时候，判断是读取结束的原因是否是：遇到⽂件尾结束。
*/

/*
1.⽂本⽂件读取是否结束，判断返回值是否为 EOF （ fgetc ），或者 NULL （ fgets ）
例如：
	• fgetc 判断是否为 EOF .
	• fgets 判断返回值是否为 NULL .
2. ⼆进制⽂件的读取结束判断，判断返回值是否⼩于实际要读的个数。
例如：
	• fread判断返回值是否⼩于实际要读的个数
*/

//文本文件
//#include <stdio.h>
//#include <stdlib.h>
//int main(void)
//{
//	int c; // 注意：int，⾮char，要求处理EOF
//	FILE* fp = fopen("test.txt", "r");
//	if (!fp) {//判断fp是否为空指针
//		perror("File opening failed");
//		return EXIT_FAILURE;
//	}
//	//fgetc 当读取失败的时候或者遇到⽂件结束的时候，都会返回EOF
//	while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环
//	{
//		putchar(c);
//	}
//	//判断是什么原因结束的
//	if (ferror(fp))//ferror 判断读取时是不是因为I/O错误导致读取结束
//		puts("I/O error when reading");
//	else if (feof(fp))//feof 判断读取时是不是因为读到文件结束标志（EOF）导致读取结束
//		puts("End of file reached successfully");
//	fclose(fp);
//}


//二进制文件
//#include <stdio.h>
//enum { SIZE = 5 };
//int main(void)
//{
//	double a[SIZE] = { 1.,2.,3.,4.,5. };
//	FILE* fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式
//	fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组
//	fclose(fp);
//	double b[SIZE];
//	fp = fopen("test.bin", "rb");
//	size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
//	if (ret_code == SIZE) {
//		puts("Array read successfully, contents: ");
//		for (int n = 0; n < SIZE; ++n)
//			printf("%f ", b[n]);
//		putchar('\n');
//	}
//	else { // error handling
//		if (feof(fp))
//			printf("Error reading test.bin: unexpected end of file\n");
//		else if (ferror(fp)) {
//			perror("Error reading test.bin");
//		}
//	}
//	fclose(fp);
//	fp = NULL;
//}

//拷贝文件
//test1.txt ---> test2.txt

//int main()
//{
//	//打开文件
//	FILE* pfread = fopen("test1.txt", "r");
//	if (pfread == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	FILE* pfwrite = fopen("test2.txt", "w");
//	if (pfwrite == NULL)
//	{
//		perror("fopen");
//		fclose(pfread);
//		pfread = NULL;
//		return 1;
//	}
//
//	//读--->写文件
//	int ch = 0;
//	while ((ch = fgetc(pfread)) != EOF)
//	{
//		fputc(ch, pfwrite);
//	}
//
//	//关闭文件
//	fclose(pfread);
//	pfread = NULL;
//	fclose(pfwrite);
//	pfwrite = NULL;
//	return 0;
//
//}


//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//读文件
//	int ch = 0;
//	while((ch = fgetc(pf)) != EOF)
//	{
//		printf("%c\n", ch);
//	}
//
//	//int ret = feof(pf);
//	//printf("%d\n", ret);
//
//	/*ret = ferror(pf);
//	printf("%d\n", ret);*/
//	//注意 ：feof 与  ferror 的返回值不可能同时为1
//	if (ferror(pf))
//	{
//		perror("ferror");//错误：文件打开方式与文件操作内容不对应，但perror貌似无法检测出这个错误
//		return 1;
//	}
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}


//****************************************************************************************8 ⽂件缓冲区

//目的：提高效率

//#include <stdio.h>
//#include <windows.h>
////VS2022 WIN11环境测试
//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	fputs("abcdef", pf);//先将代码放在输出缓冲区
//	printf("睡眠10秒-已经写数据了，打开test.txt⽂件，发现⽂件没有内容\n");
//	Sleep(10000);
//	printf("刷新缓冲区\n");
//	fflush(pf);//刷新缓冲区时，才将缓冲区的数据写到⽂件（磁盘）
//	//注：fflush 在⾼版本的VS上不能使⽤了
//	printf("再睡眠10秒-此时，再次打开test.txt⽂件，⽂件有内容了\n");
//	Sleep(10000);
//	fclose(pf);
//	//注：fclose在关闭⽂件的时候，也会刷新缓冲区
//	pf = NULL;
//	return 0;
//}
